package com.cloud.basic.nettyTest;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import java.nio.ByteBuffer;

/**
 * @program: basic
 * @description: ByteBuff和ByteBuf对比
 * @author: Cloud
 * @create: 2021/9/30 10:31:03
 */
public class ByteBufferToByteBuf {

    public static void BufferTest(){
        ByteBuffer header = ByteBuffer.allocate(1);
        header.put("a".getBytes());
        header.flip();
        ByteBuffer body = ByteBuffer.allocate(1);
        body.put("b".getBytes());
        body.flip();
        ByteBuffer message = ByteBuffer.allocate(header.remaining() + body.remaining());
        message.put(header);
        message.flip();
        while (message.hasRemaining()) {
            System.err.println((char)message.get());
        }
    }

    public static void BufTest(){
        CompositeByteBuf messageBuf = Unpooled.compositeBuffer();
        ByteBuf headBuf = Unpooled.buffer(1);
        headBuf.writeByte('a');
        ByteBuf bodyBuf = Unpooled.buffer(1);
        bodyBuf.writeByte('b');
        messageBuf.addComponents(headBuf,bodyBuf);
        for (ByteBuf buf : messageBuf) {
            System.out.println((char)buf.readByte());
            System.out.println(buf.toString());
        }
    }

    // ByteBuf引用计数
    public static void BufCount(){
        ByteBuf buffer = Unpooled.buffer(2);
        int cnt = buffer.refCnt();
        System.out.println("refCnt: " + cnt);
        buffer.retain();
        buffer.retain();
        buffer.retain();
        buffer.retain();
        cnt = buffer.refCnt();
        System.err.println("cnt = " + cnt);
        boolean release = buffer.release(2);
        int i = buffer.refCnt();
        System.err.println("refCnt : " + i + " ===== " + release);
        release = buffer.release(3);
        cnt = buffer.refCnt();
        System.err.println("refCnt：" + cnt + " ===== " + release);
    }

    public static void main(String[] args) {
        // NIO---ByteBuffer
//        BufferTest();
        // Netty---Buf
//        BufTest();
        // ByteBuf 引用计数
//        BufCount();

        // 创建ByteBuf
        // 创建一个heapBuf，是在堆内分配的
        ByteBuf heapBuf = Unpooled.buffer(5);
        if (heapBuf.hasArray()) {
            byte[] array = heapBuf.array();
            int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
            int length = heapBuf.readableBytes();
        }

        // 创建一个directBuffer, 是分配的堆外内存
        ByteBuf directBuffer = Unpooled.directBuffer();
        if (!directBuffer.hasArray()) {
            int length = directBuffer.readableBytes();
            byte[] bytes = new byte[length];
            directBuffer.getBytes(directBuffer.readerIndex(),bytes);
        }
        // a. 分配的堆外内存空间，在进行网络传输时就不用进行拷贝，直接被网卡使用。但是这些空间想要被jvm所使用，必须拷贝到堆内存中。
        // b. 分配和释放堆外内存相比堆内存而言，代价是相当昂贵的。
        // c. 使用这两者buffer中的数据的方式也略有不同,见上面的代码段。

        // 进行readByte和writeByte方法的调用时会改变readIndex和writeIndex的值，
        // 而调用set和get方法时不会改变readIndex和writeIndex的值。
        // 上面的测试案例中打印的writeIndex和readIndex均为1,并未在调用set和get方法后被改变。
        ByteBuf heapBuf1 = Unpooled.buffer(5);
        System.err.println("writeByteIndex: " + heapBuf1.writerIndex());
        heapBuf1.writeByte(1);
        heapBuf1.writeByte(1);
        System.err.println("writeByteIndex: " + heapBuf1.writerIndex());
        heapBuf1.readByte();
        heapBuf1.readByte();
        // 这个会报错 IndexOutOfBoundsException，意思是说read的地方还没有数据 write的index才到2，read的Index不能到达3
//        heapBuf1.readByte();
        System.err.println("writeByteIndex: " + heapBuf1.readerIndex());
        heapBuf1.setByte(2,2);
        System.err.println("writeByteIndex: " + heapBuf1.writerIndex());
        heapBuf1.getByte(2);
        heapBuf1.getByte(2);
        heapBuf1.getByte(2);
        System.err.println("writeByteIndex: " + heapBuf1.readerIndex());

        // 调用discardReadBytes方法后，readIndex置为0，writeIndex也往前移动了Discardable bytes长度的距离，
        // 扩大了可写区域。但是这种做法会严重影响效率，它进行了大量的拷贝工作。如果要进行数据的清除操作，
        // 建议使用clear方法。调用clear()方法将会将readIndex和writeIndex同时置为0，
        // 不会进行内存的拷贝工作，同时要注意，clear方法不会清除内存中的内容,只是改变了索引位置而已。

        // duplicate()：直接拷贝整个buffer。
        // slice()：拷贝buffer中已经写了的数据。
        // slice(index,length): 拷贝buffer中从index开始，长度为length的数据。
        // readSlice(length): 从当前readIndex读取length长度的数据。
        // 上面这几个方法的虽然是拷贝，但是这几个方法并没有实际意义上去复制一个新的buffer出来，
        // 它和原buffer是共享数据的。所以说调用这些方法消耗是很低的，并没有开辟新的空间去存储，
        // 但是修改后会影响原buffer。这种方法也就是咱们俗称的浅拷贝。
        // 要想进行深拷贝，这里可以调用copy()和copy(index,length)方法，使用方法和上面介绍的一致，
        // 但是会进行内存复制工作，效率很低。


        ByteBuf heapBuf2 = Unpooled.buffer(5);
        heapBuf2.writeByte(1);
        heapBuf2.writeByte(1);
        heapBuf2.writeByte(1);
        heapBuf2.writeByte(1);
        heapBuf2.writeByte(1);
        heapBuf2.setByte(4,6);
        // 直接拷贝整个buffer
        ByteBuf duplicate = heapBuf2.duplicate();
        duplicate.setByte(0,2);
        // duplicate：2===heapBuf：2
        System.err.println("duplicate: " + duplicate.getByte(0) + " ====== heapBuf2: " + heapBuf2.getByte(0));
        // 拷贝buffer已经写了的数据
        ByteBuf slice = heapBuf2.slice();
        // slice capacity：4
        System.err.println("slice capacity: " + slice.capacity());
        slice.setByte(2,5);
        ByteBuf slice1 = heapBuf2.slice(0, 3);
        // slice1 capacity: 3
        System.err.println("slice1.capacity()：" + slice1.capacity());
        // 深拷贝一个出来，会开辟的新的内存空间，故拷贝出来的对象和当前对象的修改是互不相影响
        ByteBuf copy = heapBuf2.copy();
        copy.setByte(3,7);
        heapBuf2.setByte(1,4);
        //duplicate: 5====heapBuf: 5
        System.err.println("duplicate: " + duplicate.getByte(2) + " ====== heapBuf2: "+heapBuf2.getByte(2));
        System.err.println("duplicate: " + copy.getByte(3) + " ====== heapBuf2: "+heapBuf2.getByte(3));
        System.err.println("duplicate: " + copy.getByte(1) + " ====== heapBuf2: "+heapBuf2.getByte(1));
    }
}
